import axios from 'axios'
import qs from 'qs'
import { download } from './public'

/**
 * axios请求框架封装
 ***/

// base url
let baseurl = ''
// 请求配置
const requestConfig = {}
// 多服务域名配置
const servicesConfig = {}
const servicesRequest = {}

// 拦截器
let successInterceptor = null
let errorInterceptor = null
let beforeInterceptor = null

/**
 * 设置请求 配置
 * @param {Object} config      初始配置
 * @param {Object} services 多个服务域名配置
 * */
const setRequestConfig = (config = {}, services = []) => {
  // 多服务域名配置
  if (services && services.length > 0) {
    for (const service of services) {
      servicesConfig[service.name] = service
    }
    baseurl = services[0].url
    createServersRequest()
  }

  // 单个服务域名配置
  if (!baseurl) {
    baseurl = config.baseURL
  }

  Object.assign(requestConfig, config)
}

/**
 * 设置拦截器
 * @param {Function} fun  拦截器函数
 * @param {String} type  拦截器类型 默认为 SUCCESS
 */
const setInterceptor = (fun, type = 'SUCCESS') => {
  if (type === 'SUCCESS' && typeof fun === 'function') {
    // 请求成功 拦截器
    successInterceptor = fun
    return
  }
  if (type === 'ERROR' && typeof fun === 'function') {
    // 请求失败 拦截器
    errorInterceptor = fun
    return
  }

  if (type === 'BEFORE' && typeof fun === 'function') {
    // 请求之前 拦截器
    beforeInterceptor = fun
    return
  }
}

class Request {
  constructor(serviceName = null, timeout) {
    // create axios
    this.instance = axios.create({
      baseURL: serviceName ? servicesConfig[serviceName].url : baseurl,
      timeout: timeout || requestConfig.timeout,
      headers: requestConfig.headers ? requestConfig.headers : {}
    })

    this.config = {
      dataType: 'json' // 默认返回json类型的数据格式
    }

    this.type = 'json' // json formdata urlencoded

    // 参数
    this.paramData = {}

    // axios 传参
    this.axiosConfig = null

    // header config
    this.config.headers = Object.assign(
      {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      requestConfig.headers
    )
  }

  /**
   * 直接用axios方法调用
   * setAxios 调用后 setParameters 无效
   * @param {Object} config axios传参
   * {
   *  url: '',
   *  method: 'get',
   *  params | data
   * }
   */
  setAxios(config) {
    return this.instanceRequest({ ...this.config, ...config })
  }

  /**
   * 设置请求参数对象
   *
   * @param {Array} obj  传参对象
   * @param {String} type 传参类型  json formdata urlencoded
   * */
  setParameters(paramData = {}, type = 'json') {
    this.paramData = paramData
    this.type = type
    return this
  }

  /**
   * 添加参数
   * @param {String} key
   * @param {String} value
   * */
  add(key, value) {
    this.paramData[key] = value
    return this
  }

  /**
   * 请求方法
   *
   * @param url 请求地址
   * @param method 方法名
   * @return 开始请求 Promise
   * */
  request(url, method, config = {}) {
    // headers先行处理
    const headers = { ...this.config.headers, ...config.headers }

    // set config
    Object.assign(this.config, config)
    this.config.headers = headers

    // base config
    this.config.url = url
    this.config.method = method

    let data = this.type === 'formdata' ? new FormData() : {}

    if (this.paramData && this.paramData.constructor === Object) {
      // 拼接请求参数
      this.paramData &&
        Object.keys(this.paramData).forEach(key => {
          const reger = new RegExp('{' + key + '}')
          if (reger.test(url)) {
            this.config.url = url.replace(reger, this.paramData[key])
          } else {
            if (this.type === 'formdata') {
              data.append(key, this.paramData[key])
            } else {
              data[key] = this.paramData[key]
            }
          }
        })
    } else {
      data = this.paramData
    }

    // params是添加到url的请求字符串中的
    // data是添加到请求体（body）中的
    if (method === 'GET') {
      this.config.params = data
    } else {
      // Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
      switch (this.type) {
        case 'json':
          this.config.data = data
          this.config.headers['Content-Type'] = 'application/json'
          break
        case 'urlencoded':
          this.config.data = qs.stringify(data)
          this.config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
          break
        case 'formdata':
          this.config.data = data
          this.config.headers['Content-Type'] = 'multipart/form-data'
          break
        default:
          this.config.data = data
          break
      }
    }

    return this.instanceRequest(this.config)
  }
  /**
   * 开始请求
   * @param {Object} config 请求参数
   * @returns Promise
   */
  instanceRequest(config) {
    return new Promise((resolve, reject) => {
      beforeInterceptor && beforeInterceptor(config, resolve, reject)
      this.instance(config)
        .then(response => {
          if (response.status === 200) {
            successInterceptor && successInterceptor(response, resolve, reject)
            resolve(response.data)
          } else {
            console.error('HTTP请求失败,请求地址:' + this.config.url + ',操作失败，失败信息:', response.statusText)
            reject(response)
          }
        })
        .catch(error => {
          errorInterceptor && errorInterceptor(error, resolve, reject)
          console.error('HTTP请求失败,请求地址:' + this.config.url + ',请求出现错误，异常信息', error)
          reject(error)
        })
    })
  }

  /**
   * post请求
   * @param {String} url    请求地址
   * @param {Object} config 请求配置
   * @return promise
   * */
  post(url, config) {
    return this.request(url, 'POST', config)
  }

  /**
   * get请求
   * @param {String} url    请求地址
   * @param {Object} config 请求配置
   * @return promise
   * */
  get(url, config) {
    return this.request(url, 'GET', config)
  }

  /**
   * put请求
   * @param {String} url    请求地址
   * @param {Object} config 请求配置
   * @return promise
   * */
  put(url, config) {
    return this.request(url, 'PUT', config)
  }

  /**
   * delete请求
   * @param {String} url    请求地址
   * @param {Object} config 请求配置
   * @return promise
   * */
  delete(url, config) {
    return this.request(url, 'DELETE', config)
  }

  /**
   * 文件请求
   * @param {String} url    请求地址
   * @param {Object} config 请求配置
   * @return promise
   */
  file(url, config) {
    return new Promise((resolve, reject) => {
      try {
        config = {
          method: 'GET',
          responseType: 'blob',
          ...config
        }
        this.request(url, config.method, config)
          .then(res => {
            // 下载文件
            download(res, config.fileType, config.fileName)
            resolve(res, config)
          })
          .catch(e => reject(e))
      } catch (e) {
        reject(e)
      }
    })
  }
  /**
   * 文件url 打开
   * @param url 请求地址
   * @param openUrl 是否直接打开url
   * @return promise
   */
  fileUrl(url, openUrl = true) {
    return new Promise((resolve, reject) => {
      try {
        const href = `${this.instance.defaults.baseURL + url}?${qs.stringify(this.parameters)}`
        if (openUrl) {
          console.log('下载文件流:', href)
          window.open(href)
        }
        resolve(href)
      } catch (e) {
        reject(e)
      }
    })
  }
}

/**
 * 创建请求对象
 * @param {String} serviceName 服务域名类型
 * @param {Number} timeout 超时时间
 * @returns
 */
const createRequest = (serviceName, timeout) => {
  return new Request(serviceName, timeout)
}

/**
 * 创建不同服务的请求
 */
const createServersRequest = () => {
  for (const key in servicesConfig) {
    servicesRequest[key] = () => {
      return new Request(key)
    }
  }
}
export { createRequest, setRequestConfig, setInterceptor, servicesRequest }
